package org.bdware.doip.endpoint.doipServer;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import org.apache.log4j.Logger;
import org.bdware.doip.core.codec.DelimiterMessageServerCodec;
import org.bdware.doip.core.crypto.GlobalCertifications;
import org.bdware.doip.core.utils.GlobalConfigurations;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLEngine;

public class NettyTLSDoipListener extends NettyDoipListener {

    private final int port;
    static Logger logger = Logger.getLogger(NettyTLSDoipListener.class);
    private Channel ch;
    private static final byte[] EOF = {'#', '\n' ,  '#'};

    protected NettyTLSDoipListener(int port){
        this.port = port;
    }

    @Override
    public void start() {
        if(handler == null){
            logger.error("Handler not init yet! set handler first");
            return;
        }
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(port);
            b.childHandler(
                    new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            SSLEngine engine = getSSLContext().newEngine(ch.alloc());
                                engine.setUseClientMode(false);
                                engine.setNeedClientAuth(GlobalCertifications.needAuthentication);
                            ch.pipeline()
                                    .addFirst("ssl", new SslHandler(engine))
                                    .addLast(new DelimiterBasedFrameDecoder(GlobalConfigurations.tcpFrameLength, Unpooled.wrappedBuffer(EOF)))
                                    .addLast(new DelimiterMessageServerCodec())
                                    .addLast(handler);
                        }
                    });
            ch = b.bind().syncUninterruptibly().channel();
            logger.info("TLS DOIP listener start at:" + port);
            ch.closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
        }
    }

    @Override
    public void stop() {
        ch.close();
    }

    protected SslContext getSSLContext() throws Exception {
        KeyManagerFactory keyManagerFactory;

        keyManagerFactory = KeyManagerFactory.getInstance("SunX509");

        if(GlobalCertifications.getGlobalKeyStore() == null){
            throw new Exception("keyPair not initialized yet! ");
        }
        keyManagerFactory.init(GlobalCertifications.getGlobalKeyStore(), GlobalConfigurations.JKSPassword.toCharArray());

        return SslContextBuilder.forServer(keyManagerFactory).trustManager(InsecureTrustManagerFactory.INSTANCE).build();

    }
}
